/**
  ******************************************************************************
  * @author     Chris
  * @since      2024/6/17 15:13
  *
  * @file       pd_timer_ic.c
  * @brief      Timer IC peripheral driver.
  *
  * @note       This file contains the peripheral driver for the Timer IC.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date          Author       Notes
  *   2024/6/17     Chris        the first version
  *
  ******************************************************************************
  */

#include <stm32f4xx_ll_tim.h>
#include "pd_timer_ic.h"


static void TimerIC_init(TimerIC *this, uint8_t isPWMI) {
    GPIO **gpio = this->timerCH->gpios;
    for (int i = 0; i < this->timerCH->gpioSize; ++i) {
        gpio[i]->init(gpio[i]);
    }

    LL_TIM_SetClockSource(this->timer->TIMx, LL_TIM_CLOCKSOURCE_INTERNAL);

    // Timer Base Unit
    this->timer->init(this->timer);


    LL_TIM_IC_InitTypeDef TIM_IC_InitStruct = {0};

    LL_TIM_IC_StructInit(&TIM_IC_InitStruct);
    TIM_IC_InitStruct.ICPolarity = this->ICPolarity;
    TIM_IC_InitStruct.ICActiveInput = this->ICActiveInput;
    TIM_IC_InitStruct.ICPrescaler = this->ICPrescaler;
    TIM_IC_InitStruct.ICFilter = this->ICFilter;

    uint32_t *channel = this->timerCH->channels;
    for (int i = 0; i < this->timerCH->chSize; ++i) {
        if (i > 0 && isPWMI) {
            // PWMI mode
            TIM_IC_InitStruct.ICPolarity = LL_TIM_IC_POLARITY_FALLING;      // reverse Channel1
            TIM_IC_InitStruct.ICActiveInput = LL_TIM_ACTIVEINPUT_INDIRECTTI;// Channel Indirect
            LL_TIM_IC_Init(this->timer->TIMx, channel[i], &TIM_IC_InitStruct);
            continue;
        }
        LL_TIM_IC_Init(this->timer->TIMx, channel[i], &TIM_IC_InitStruct);
    }

    // Select Trigger Source
    LL_TIM_SetTriggerInput(this->timer->TIMx, LL_TIM_TS_TI1FP1);
    // Set Slave Mode: Reset(clear CCR)
    LL_TIM_SetSlaveMode(this->timer->TIMx, LL_TIM_SLAVEMODE_RESET);

    LL_TIM_EnableCounter(this->timer->TIMx);
}

static uint32_t TimerIC_getCCR(TimerIC *this, uint32_t channel) {
    switch (channel) {
        case LL_TIM_CHANNEL_CH1:
            return LL_TIM_IC_GetCaptureCH1(this->timer->TIMx);
        case LL_TIM_CHANNEL_CH2:
            return LL_TIM_IC_GetCaptureCH2(this->timer->TIMx);
        case LL_TIM_CHANNEL_CH3:
            return LL_TIM_IC_GetCaptureCH3(this->timer->TIMx);
        case LL_TIM_CHANNEL_CH4:
            return LL_TIM_IC_GetCaptureCH4(this->timer->TIMx);
        default:
            return 0;
    }
}

/**
 * 获取输入频率
 *
 * @brief 输入频率 = 标准频率 / 计次数    其中，
 *        标准频率：指预分频后的内部时钟频率。这里的预分频包括时基单元与输入捕获单元的分频。
 *        计次数：输入捕获单元对接收到的信号计数
 *
 * @param this
 * @return
 */
static uint32_t TimerIC_getFreq(TimerIC *this) {
    uint32_t ccr = TimerIC_getCCR(this, this->timerCH->channels[0]);
    if (ccr == 0) return 0;
    // 输入频率 = 标准频率 / 计次数
    if (this->timer->TIMx == TIM1 || this->timer->TIMx == TIM8 || this->timer->TIMx == TIM9 ||
        this->timer->TIMx == TIM10 || this->timer->TIMx == TIM11)
        return 168000000 / (this->timer->Prescaler + 1) * (this->ICPrescaler + 1) / ccr;
    else
        return 84000000 / (this->timer->Prescaler + 1) * (this->ICPrescaler + 1) / ccr;
}

static uint32_t TimerIC_getDuty(TimerIC *this) {
    uint32_t ccr1 = TimerIC_getCCR(this, this->timerCH->channels[0]) + 1;
    if (ccr1 == 0) return 0;

    uint32_t ccr2 = TimerIC_getCCR(this, this->timerCH->channels[1]) + 1;
    return ccr2 * 100 / ccr1;
}

static TimerIC *build(Timer *timer, TimerCh *timerChannel) {
    TimerIC *gpio = malloc(sizeof(TimerIC));

    Driver_add(gpio, GEN_PERM);

    gpio->timer         = timer;
    gpio->timerCH  = timerChannel;
    gpio->ICPolarity    = LL_TIM_IC_POLARITY_RISING;
    gpio->ICActiveInput = LL_TIM_ACTIVEINPUT_DIRECTTI;
    gpio->ICPrescaler   = LL_TIM_ICPSC_DIV1;
    gpio->ICFilter      = 0xF;

    gpio->init     = &TimerIC_init;
    gpio->getFreq = &TimerIC_getFreq;
    gpio->getDuty = &TimerIC_getDuty;

    return gpio;
}

const struct TimerICClass TimerICBuilder = {.build = &build};